/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.data;

import javautil.lang.MathUtil;

import java.io.Serializable;

/**
 * Provides a model for a physical region in DNA. Clones, Probes and Genes are
 * examples of such models.
 * @author Susanta Tewari
 * @version 1.0
 * @created 03-Nov-2007 2:46:49 AM
 */
public abstract class DNA_Region implements Serializable {

    /**
     * static type constatnt for clone
     */
    static final String CLONE_TYPE = "CONE";

    /**
     * static type constatnt for gene
     */
    public static final String GENE_TYPE = "GENE";

    /**
     * static type constatnt for probe
     */
    static final String PROBE_TYPE = "PROBE";

    /**
     * default type for DNA_Region
     */
    private static final String DEFAULT_TYPE = "DNA_REGION";

    /**
     * The chromosome it belongs to : valid values (integers) start from
     * 1. For Neurospora crassa it is one of the integers from 1 to 7 including.
     */
    private int chId = 0;

    /**
     * position of the tail of the segment in bp(base pair).
     */
    private int endPos = 0;

    /**
     * Name of the region. For example, name of a gene or clone.
     */
    private String label = null;

    /**
     * Reference order:
     * For clones: clone order in the hybrid data and for genes the order in the rflp
     * data.
     */
    private int order = -1;

    /**
     * head position of the segment in bp.
     */
    private int startPos = 0;

    /**
     * denoting the type of DNA_Region .
     */
    private String type = null;

    /**
     * Constructs ...
     */
    DNA_Region() {
        setType(DEFAULT_TYPE);
    }

    /**
     * checks if the region has any sequence information available.
     * @return true if and only if  <code>startPos</code>  and <code>endPos</code>
     * are both positive
     */
    public boolean hasSeqInfo() {
        return (startPos > 0) && (endPos > 0);
    }

    @Override
    public String toString() {

        String sep          = System.getProperty("line.separator");
        StringBuffer buffer = new StringBuffer();

        buffer.append(sep);
        buffer.append("chId = ");
        buffer.append(chId);
        buffer.append("endPos = ");
        buffer.append(endPos);
        buffer.append(sep);
        buffer.append("label = ");
        buffer.append(label);
        buffer.append(sep);
        buffer.append("order = ");
        buffer.append(order);
        buffer.append(sep);
        buffer.append("startPos = ");
        buffer.append(startPos);
        buffer.append(sep);

        return buffer.toString();
    }

    int getEndPos() {
        return endPos;
    }

    public String getLabel() {
        return label;
    }

    public int getOrder() {
        return order;
    }

    public int getStartPos() {
        return startPos;
    }

    public void setEndPos(int newVal) {
        endPos = newVal;
    }

    public void setLabel(String newVal) {
        label = newVal;
    }

    public void setOrder(int newVal) {
        order = newVal;
    }

    public void setStartPos(int newVal) {
        startPos = newVal;
    }

    /**
     * checks if there is a common region.
     * @param region
     * @return true if and only if there is a common region.
     */
    public boolean isOverLapping(DNA_Region region) {

        if (hasSeqInfo() && region.hasSeqInfo()) {

            if (MathUtil.contains(region.getStartPos(), this.startPos, this.endPos)
                    || MathUtil.contains(region.getEndPos(), this.startPos, this.endPos)
                    || MathUtil.contains(this.startPos, region.getStartPos(), region.getEndPos())
                    || MathUtil.contains(this.endPos, region.getStartPos(), region.getEndPos())) {
                return true;
            }

            return false;
        }

        return false;
    }

    /**
     * Tests this abstract region for equality with the given object. Returns <code>true</code>
     * if and only if the argument is not <code>null</code> and is an abstract region
     * that denotes the same <code>label</code> and sequence information as this abstract region
     *
     * @param obj The object to be compared with this abstract region
     */
    @Override
    public boolean equals(Object obj) {

        boolean result = false;

        if (obj instanceof DNA_Region) {

            DNA_Region region = (DNA_Region) obj;

            result = region.getLabel().equals(label) && (region.getChId() == chId);

            if (result && hasSeqInfo())
                result = (region.getStartPos() == startPos) && (region.getEndPos() == endPos);
        }

        return result;
    }

    public String getType() {
        return type;
    }

    void setType(String newVal) {
        type = newVal;
    }

    /**
     * Computes a hash code for this abstract region. The hascode is equal to the
     * addition of hascodes of the label and sequence information
     * @return A hash code for this abstract region
     */
    @Override
    public int hashCode() {

        int result = 0;

        result += getLabel().hashCode();

        if (hasSeqInfo())
            result += new Integer(startPos).hashCode() + new Integer(endPos).hashCode();

        return result;
    }

    /**
     * tests against the defualt value of order. Checks if any order information
     * is set on this abstract region which is used by some ordering tools for example,
     * Simulated Annealing and Random Cost algorithms.
     * @return true if any order information is set.
     */
    public boolean hasOrderInfo() {
        return (order != -1) && (order > 0);
    }

    public int getChId() {
        return chId;
    }

    public void setChId(int chId) {
        this.chId = chId;
    }
}
